﻿Option Explicit On

Imports Microsoft.VisualBasic
Imports Microsoft.VisualBasic.FileIO
Imports System.IO
Imports JPC.Medialibrary.DAL
Imports System.Data
Imports HundredMilesSoftware.UltraID3Lib
Imports SubSonic

Namespace JPC.Medialibrary.BLL

    Public Class MedialibraryManager

        Public Shared Function GetAlbumsByArtist(ByVal artistId As Integer) As List(Of Album)
            Return SPs.GetAlbumsByArtist(artistId).ExecuteTypedList(Of Album)()
        End Function

        Public Shared Function GetAlbumsByTag(ByVal tagId As Integer) As List(Of Album)
            Return SPs.GetAlbumsByTag(tagId).ExecuteTypedList(Of Album)()
        End Function

        Public Shared Function GetAlbumsByText(ByVal searchText As String) As List(Of Album)
            Return SPs.GetAlbumsByText(searchText).ExecuteTypedList(Of Album)()
        End Function

        Public Shared Function GetAlbumsByInitial(ByVal initial As Integer) As List(Of Album)
            If initial = 0 Then
                Return SPs.GetAlbumsByInitial("0", "9").ExecuteTypedList(Of Album)()
            Else
                Return SPs.GetAlbumsByInitial(Chr(initial), Chr(initial)).ExecuteTypedList(Of Album)()
            End If
        End Function

        Public Shared Function GetArtistsByTag(ByVal tagId As Integer) As List(Of Artist)
            Return SPs.GetArtistsByTag(tagId).ExecuteTypedList(Of Artist)()
        End Function

        Public Shared Function GetArtistsByText(ByVal searchText As String) As List(Of Artist)
            Return SPs.GetArtistsByText(searchText).ExecuteTypedList(Of Artist)()
        End Function

        Public Shared Function GetArtistsByInitial(ByVal initial As Integer) As List(Of Artist)
            If initial = 0 Then
                Return SPs.GetArtistsByInitial("0", "9").ExecuteTypedList(Of Artist)()
            Else
                Return SPs.GetArtistsByInitial(Chr(initial), Chr(initial)).ExecuteTypedList(Of Artist)()
            End If
        End Function

        Public Shared Function GetArtistsByAlbum(ByVal albumId As Integer) As List(Of Artist)
            Return SPs.GetArtistsByAlbum(albumId).ExecuteTypedList(Of Artist)()
        End Function

        Public Shared Function GetPlayListsByText(ByVal searchText As String) As List(Of Playlist)
            Return SPs.GetPlayListsByText(searchText).ExecuteTypedList(Of Playlist)()
        End Function

        Public Shared Function GetAllPlaylists() As PlaylistCollection
            Dim p As New PlaylistController
            Return p.FetchAll().OrderByAsc("PlayListName")
        End Function

        Public Shared Function GetSongsByAlbum(ByVal albumId As Integer) As List(Of Song)
            Return SPs.GetSongsByAlbum(albumId).ExecuteTypedList(Of Song)()
        End Function

        Public Shared Function GetSongsByArtist(ByVal artistId As Integer) As List(Of Song)
            Return SPs.GetSongsByArtist(artistId).ExecuteTypedList(Of Song)()
        End Function

        Public Shared Function GetSongsByAlbumArtist(ByVal albumId As Integer, ByVal artistId As Integer) As List(Of Song)
            Return SPs.GetSongsByAlbumArtist(albumId, artistId).ExecuteTypedList(Of Song)()
        End Function

        Public Shared Function GetSongsByAlbumTag(ByVal albumId As Integer, ByVal tagId As Integer) As List(Of Song)
            Return SPs.GetSongsByAlbumTag(albumId, tagId).ExecuteTypedList(Of Song)()
        End Function

        Public Shared Function GetSongsByPlaylist(ByVal playlistId As Integer) As List(Of Song)
            Return SPs.GetSongsByPlaylist(playlistId).ExecuteTypedList(Of Song)()
        End Function

        Public Shared Function GetSongsByTag(ByVal tagId As Integer) As List(Of Song)
            Return SPs.GetSongsByTag(tagId).ExecuteTypedList(Of Song)()
        End Function

        Public Shared Function GetSongsByText(ByVal searchText As String) As List(Of Song)
            Return SPs.GetSongsByText(searchText).ExecuteTypedList(Of Song)()
        End Function

        Public Shared Function GetSongsByInitial(ByVal initial As Integer) As List(Of Song)
            If initial = 0 Then
                Return SPs.GetSongsByInitial("0", "9").ExecuteTypedList(Of Song)()
            Else
                Return SPs.GetSongsByInitial(Chr(initial), Chr(initial)).ExecuteTypedList(Of Song)()
            End If
        End Function

        Public Shared Function GetTagsByText(ByVal searchText As String) As List(Of Tag)
            Return SPs.GetTagsByText(searchText).ExecuteTypedList(Of Tag)()
        End Function

        Public Shared Function GetTagsByInitial(ByVal initial As Integer) As List(Of Tag)
            If initial = 0 Then
                Return SPs.GetTagsByInitial("0", "9").ExecuteTypedList(Of Tag)()
            Else
                Return SPs.GetTagsByInitial(Chr(initial), Chr(initial)).ExecuteTypedList(Of Tag)()
            End If
        End Function

        Public Shared Function GetAllTags() As TagCollection
            Dim t As New TagController
            Dim c As TagCollection = t.FetchAll
            c.Sort("TagName", True)
            Return c
        End Function

        Public Shared Sub SavePlaylist(ByVal varPlayList As List(Of Song), ByVal playlistName As String)

            Dim _playlist As New Playlist
            Dim _playlistSong As PlaylistSong
            _playlist.LoadByParam(Playlist.Columns.PlayListName, playlistName)
            If _playlist.IsNew Then
                _playlist.PlayListName = playlistName
                _playlist.Save()
            Else
                PlaylistSong.Delete(_playlist.PlaylistId)
            End If
            Dim n As Integer = 0
            For Each _song As Song In varPlayList
                _playlistSong = New PlaylistSong
                _playlistSong.PlaylistId = _playlist.PlaylistId
                _playlistSong.SongId = _song.SongId
                _playlistSong.Orden = n
                _playlistSong.Save()
                n += 1
            Next

        End Sub

        Public Shared Sub DeletePlaylist(ByVal playlistId As Integer)

            PlaylistSong.Delete(playlistId)
            Playlist.Delete(playlistId)

        End Sub

        Public Shared Sub ModifyTag(ByVal oldTagID As Integer, ByVal newTagName As String)

            newTagName = StrConv(newTagName, VbStrConv.ProperCase).Trim

            Dim _tag As New Tag(oldtagID)
            If _tag.TagName.Trim = newtagName Then
                Exit Sub
            End If

            Dim oldTagName As String = _tag.TagName.Trim
            Dim newTagId As Integer = 0
            Dim updateDB As Boolean = False

            _tag.LoadByParam(Tag.Columns.TagName, newTagName)
            'Si el nuevo nombre no existe en la base cambio la
            'descripción del tag 
            If _tag.IsNew Then
                newTagId = oldTagID
                _tag.LoadByKey(newTagId)
                _tag.TagName = newTagName
                _tag.Save()

                'Si el nuevo nombre existe en la base entonces reemplazo
                'el tag anterior por el nuevo y borro el tag de la base
            Else
                newTagId = _tag.TagId
                updateDB = newTagId <> oldTagID
            End If

            'Actualizo los tags de los mp3
            UpdateID3Tag(oldTagID, oldTagName, newTagName)

            If updateDB Then
                SPs.UpdateSongTagId(oldTagId, newTagId).Execute()
                Tag.Delete(oldTagID)
            End If

        End Sub

        Private Shared Sub UpdateID3Tag(ByVal tagId As Integer, ByVal oldTagName As String, ByVal newTagName As String)

            Dim _songs As List(Of Song) = SPs.GetSongsByTag(tagId).ExecuteTypedList(Of Song)()

            For Each _song As Song In _songs

                Dim mp3 As UltraID3 = New UltraID3
                mp3.Read(_song.FileName)

                Dim sOldTags As String = GetTags(mp3)
                Dim sNewTags As String = ""

                If sOldTags <> "" Then
                    Dim aTags() As String
                    If sOldTags.IndexOf(",") = 0 Then
                        aTags = New String(0) {sOldTags}
                    Else
                        aTags = sOldTags.Split(",")
                    End If

                    For Each sTag In aTags
                        If sTag.Trim <> oldTagName And _
                        sTag <> newTagName And _
                        Mid(oldTagName, 1, Len(CStr(mp3.ID3v1Tag.Genre))) <> CStr(mp3.ID3v1Tag.Genre) Then
                            sNewTags = sNewTags & IIf(sNewTags = "", "", ",") & sTag.Trim
                        End If
                    Next

                    sNewTags = sNewTags & IIf(sNewTags = "", "", ",") & newTagName

                    mp3 = Nothing

                    Dim mp3_2 As New UltraID3
                    mp3_2.Read(_song.FileName)
                    mp3_2.ID3v2Tag.Genre = sNewTags
                    mp3_2.Write()

                End If

            Next

        End Sub

        Public Shared Sub ModifyArtist(ByVal oldArtistID As Integer, ByVal newArtistName As String)

            'newArtistName = StrConv(newArtistName, VbStrConv.ProperCase).Trim
            newArtistName = SanitizeString(newArtistName)

            Dim _artist As New Artist(oldArtistID)
            If _artist.ArtistName.Trim = newArtistName Then
                Exit Sub
            End If

            Dim oldArtistName As String = _artist.ArtistName.Trim
            Dim newArtistId As Integer = 0
            Dim updateDB As Boolean = False

            _artist.LoadByParam(Artist.Columns.ArtistName, newArtistName)
            'Si el nuevo nombre no existe en la base cambio la
            'descripción del tag 
            If _artist.IsNew Then
                newArtistId = oldArtistID
                _artist.LoadByKey(newArtistId)
                _artist.ArtistName = Mid(newArtistName, 1, 255)
                _artist.Save()

                'Si el nuevo nombre existe en la base entonces reemplazo
                'el tag anterior por el nuevo y borro el artista de la base
            Else
                newArtistId = _artist.ArtistId
                updateDB = newArtistId <> oldArtistID
            End If

            'Actualizo los tags de los mp3
            UpdateID3Artist(oldArtistID, newArtistName)

            If updateDB Then
                SPs.UpdateSongArtistId(oldArtistID, newArtistId).Execute()
                Artist.Delete(oldArtistID)
            End If

        End Sub

        Private Shared Sub UpdateID3Artist(ByVal artistId As Integer, ByVal newArtistName As String)

            Dim _songs As List(Of Song) = SPs.GetSongsByArtist(artistId).ExecuteTypedList(Of Song)()

            For Each _song As Song In _songs
                Dim mp3 As New UltraID3
                mp3.Read(_song.FileName)
                mp3.ID3v2Tag.Artist = newArtistName
                mp3.Write()
            Next

        End Sub

        Public Shared Sub ModifySong(ByVal songID As Integer, ByVal newSongTitle As String, ByVal newAlbumName As String, ByVal newArtistName As String) ', ByVal newTags As String)

            'newSongTitle = StrConv(newSongTitle, VbStrConv.ProperCase).Trim
            'newAlbumName = StrConv(newAlbumName, VbStrConv.ProperCase).Trim
            'newArtistName = StrConv(newArtistName, VbStrConv.ProperCase).Trim

            newSongTitle = SanitizeString(newSongTitle)
            newAlbumName = SanitizeString(newAlbumName)
            newArtistName = SanitizeString(newArtistName)

            'newTags = StrConv(newTags, VbStrConv.ProperCase).Trim

            Dim _song As New Song(songID)

            Dim artistUpdated As Boolean = False
            Dim albumUpdated As Boolean = False
            Dim songTitleUpdated As Boolean = False

            Dim oldArtistId As Integer = _song.ArtistId
            Dim oldAlbumId As Integer = _song.AlbumId

            'Verifico si se cambió el nombre de la canción
            If _song.Title.Trim <> newSongTitle Then
                _song.Title = newSongTitle
                songTitleUpdated = True
            End If

            'Verifico si se cambió el artista
            If _song.ArtistName.Trim <> newArtistName Then
                Dim _artist As New Artist
                _artist.LoadByParam(Artist.Columns.ArtistName, newArtistName)
                'Si el artista ingresado no existe en la base creo uno nuevo
                If _artist.IsNew Then
                    _artist.ArtistName = newArtistName
                    _artist.Save()
                End If
                'Actualizo el código del artista en la tabla
                _song.ArtistId = _artist.ArtistId
                artistUpdated = True
            End If

            'Verifico si se cambió el album
            If _song.AlbumName.Trim <> newAlbumName Then
                Dim _album As New Album
                _album.LoadByParam(Album.Columns.AlbumName, newAlbumName)
                'Si el artista ingresado no existe en la base creo uno nuevo
                If _album.IsNew Then
                    _album.AlbumName = Mid(newAlbumName, 1, 255)
                    _album.Save()
                End If
                'Actualizo el código del artista en la tabla
                _song.AlbumId = _album.AlbumId
                albumUpdated = True
            End If

            'Actualizo la base de datos y los tags del mp3
            If songTitleUpdated Or albumUpdated Or artistUpdated Then
                _song.Save()

                'Verifica si el artista ya no tiene mas canciones
                If artistUpdated Then
                    Dim cancionesDelArtista As Integer = _
                        New [Select]() _
                        .From(Tables.Song) _
                        .Where(Song.ArtistIdColumn).IsEqualTo(oldArtistId) _
                        .GetRecordCount()
                    'Si no tiene mas canciones lo borra
                    If cancionesDelArtista = 0 Then
                        Artist.Delete(oldArtistId)
                    End If
                End If

                'Verifica si el album ya no tiene mas canciones
                If albumUpdated Then
                    Dim cancionesDelAlbum As Integer = _
                        New [Select]() _
                        .From(Tables.Song) _
                        .Where(Song.AlbumIdColumn).IsEqualTo(oldAlbumId) _
                        .GetRecordCount()
                    'Si no tiene mas canciones lo borra
                    If cancionesDelAlbum = 0 Then
                        Album.Delete(oldAlbumId)
                    End If
                End If

                'Por último actualiza el mp3
                Dim mp3 As New UltraID3
                mp3.Read(_song.FileName)
                mp3.ID3v2Tag.Title = newSongTitle
                mp3.ID3v2Tag.Artist = newArtistName
                mp3.ID3v2Tag.Album = newAlbumName
                'mp3.ID3v2Tag.Genre = newTags
                mp3.Write()
            End If

        End Sub

        Public Shared Sub CleanMusicDB()

            SPs.LimpiarDB.Execute()

        End Sub

        Public Shared Sub UpdateMusicDB()

            If ProcessingStatus.Processing Then
                Exit Sub
            End If

            ProcessingStatus.Processing = True

            ProcessingStatus.AlbumsQty = 0
            ProcessingStatus.ArtistsQty = 0
            ProcessingStatus.TagsQty = 0
            ProcessingStatus.SongsQty = 0

            'Busca en todos los discos del sistema
            For Each drive As DriveInfo In My.Computer.FileSystem.Drives
                'Busca si existe una carpeta Medialibrary
                If drive.DriveType = DriveType.Fixed Then
                    If My.Computer.FileSystem.DirectoryExists(drive.Name + "Medialibrary\Music") Then
                        InspectDirectory(drive.Name + "Medialibrary\Music")
                    End If
                End If
            Next

            ProcessingStatus.Processing = False

        End Sub

        Private Shared Sub InspectDirectory(ByVal dirPath As String)

            ProcessingStatus.Folder = dirPath

            'Solo Procesa las carpetas que no hayan sido procesadas antes
            'o todas si cleanDB es True
            Dim cantidad As Integer = _
                    New [Select]() _
                    .From(Song.Schema) _
                    .Where("FileName").Like(dirPath & "%").GetRecordCount

            'Primero procesa todos los mp3 de la carpeta
            If cantidad = 0 Then
                For Each file As String In My.Computer.FileSystem.GetFiles(dirPath, FileIO.SearchOption.SearchTopLevelOnly, "*.mp3")
                    ProcesarMP3(file)
                    System.Threading.Thread.Sleep(10)
                Next
            End If

            'Después procesa las subcarpetas
            For Each Dir As String In My.Computer.FileSystem.GetDirectories(dirPath)
                InspectDirectory(Dir)
            Next

        End Sub

        Private Shared Sub ProcesarMP3(ByVal fileName As String)

            Dim mp3 As UltraID3 = New UltraID3
            mp3.Read(fileName)

            Dim albumName As String = SanitizeString(mp3.ID3v2Tag.Album)
            Dim _album As New Album()
            If albumName = "" Then
                _album.LoadByKey(0)
            Else
                _album.LoadByParam(Album.Columns.AlbumName, albumName)
                If _album.IsNew Then
                    _album.AlbumName = Mid(albumName, 1, 255).Trim
                    _album.Save()
                    ProcessingStatus.AlbumsQty += 1
                End If
            End If

            Dim artistName As String = SanitizeString(mp3.ID3v2Tag.Artist)
            Dim _artist As New Artist()
            If artistName = "" Then
                _artist.LoadByKey(0)
            Else
                _artist.LoadByParam(Artist.Columns.ArtistName, artistName)
                If _artist.IsNew Then
                    _artist.ArtistName = Mid(artistName, 1, 255).Trim
                    _artist.Save()
                    ProcessingStatus.ArtistsQty += 1
                End If
            End If

            Dim _song As New Song
            _song.AlbumId = _album.AlbumId
            _song.ArtistId = _artist.ArtistId
            _song.Genre = mp3.ID3v1Tag.Genre
            Dim songName As String = SanitizeString(mp3.ID3v2Tag.Title)
            _song.Title = Mid(songName, 1, 255).Trim
            If Not mp3.ID3v1Tag.TrackNum = Nothing Then
                _song.Track = CShort(mp3.ID3v1Tag.TrackNum)
            End If
            If Not mp3.ID3v1Tag.Year = Nothing Then
                _song.Year = mp3.ID3v1Tag.Year
            End If
            _song.FileName = fileName.Trim

            _song.Save()
            ProcessingStatus.SongsQty += 1

            Dim sTags As String = GetTags(mp3)

            Dim _tag As Tag
            Dim _songTag As SongTag
            If sTags <> "" Then
                Dim aTags() As String
                If sTags.IndexOf(",") = 0 Then
                    aTags = New String(0) {sTags}
                Else
                    aTags = sTags.Split(",")
                End If
                For Each sTag In aTags
                    _tag = New Tag()
                    sTag = SanitizeString(sTag)
                    _tag.LoadByParam(Tag.Columns.TagName, sTag)
                    If _tag.IsNew Then
                        _tag.TagName = sTag
                        _tag.Save()
                        ProcessingStatus.TagsQty += 1
                    End If
                    _songTag = New SongTag()
                    _songTag.SongId = _song.SongId
                    _songTag.TagId = _tag.TagId
                    _songTag.Save()
                Next
            Else
                _tag = New Tag()
                _tag.LoadByKey(0)
                _songTag = New SongTag()
                _songTag.SongId = _song.SongId
                _songTag.TagId = _tag.TagId
                _songTag.Save()
            End If

            mp3 = Nothing

        End Sub

        Private Shared Function GetTags(ByVal mp3 As UltraID3) As String

            Dim sTags As String = SanitizeString(mp3.ID3v2Tag.Genre.Replace("-", " ").Replace("(", "").Replace(")", ""))
            If Mid(sTags, 1, Len(CStr(mp3.ID3v1Tag.Genre))) = CStr(mp3.ID3v1Tag.Genre) Then
                sTags = mp3.ID3v1Tag.Genre
            End If
            If sTags.Length > 0 Then
                If IsNumeric(sTags) Then
                    sTags = GetGenreName(CInt(sTags))
                End If
            End If

            Return sTags

        End Function

        Private Shared Function SanitizeString(ByVal strText As String) As String

            strText = strText.Trim

            If strText.Length > 0 Then
                'Me aseguro que las palabras estén separadas por un sólo espacio
                Do While (strText.IndexOf(Space(2)) >= 0)
                    strText = strText.Replace(Space(2), Space(1))
                Loop

                'Verifico que no termine en un caracter extraño
                'If Asc(strText.Substring(strText.Length - 1, 1)) = 63 Then
                '    strText = strText.Substring(0, strText.Length - 1)
                'End If

                'Convierto a ProperCase
                strText = StrConv(strText, VbStrConv.ProperCase)

                'Que no tenga mas de 255 char
                If strText.Length > 254 Then strText.Substring(0, 254)

                'Mando los "The","El","Los","La" y "Las" al final para que en listado alfabético aparezcan "The Beatles" en la "B"
                If Mid(strText, 1, 4) = "The " Or _
                    Mid(strText, 1, 3) = "El " Or _
                    Mid(strText, 1, 3) = "La " Or _
                    Mid(strText, 1, 4) = "Los " Or _
                    Mid(strText, 1, 4) = "Las " Then
                    strText = Mid(strText, strText.IndexOf(" ") + 1).Trim & " " & Mid(strText, 1, strText.IndexOf(" ")).Trim
                End If

            End If

            Return strText

        End Function

        Private Shared Function GetGenreName(ByVal genreNbr As Integer) As String

            Dim aGenres As String() = _
                {"Blues", "Classic Rock", "Country", "Dance", "Disco", _
                "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", _
                "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", _
                "Techno", "Industrial", "Alternative", "Ska", "Death Metal", _
                "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", _
                "Vocal", "Jazz Funk", "Fusion", "Trance", "Classical", _
                "Instrumental", "Acid", "House", "Game", "Sound Clip", _
                "Gospel", "Noise", "Alternative Rock", "Bass", "Soul", _
                "Punk", "Space", "Meditative", "Instrumental Pop", _
                "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", _
                "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", _
                "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", _
                "Top 40", "Christian Rap", "Pop/Funk", "Jungle", _
                "Native American", "Cabaret", "New Wave", "Psychadelic", _
                "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal", _
                "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", _
                "Rock & Roll", "Hard Rock", "Folk", "Folk-Rock", _
                "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", _
                "Revival", "Celtic", "Bluegrass", "Avantgarde", _
                "Gothic Rock", "Progressive Rock", "Psychedelic Rock", _
                "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", _
                "Easy Listening", "Acoustic", "Humour", "Speech", _
                "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", _
                "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam", _
                "Club", "Tango", "Samba", "Folklore", "Ballad", _
                "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", _
                "Punk Rock", "Drum Solo", "A capella", "Euro-House", _
                "Dance Hall"}

            If genreNbr <= aGenres.Length Then
                Return aGenres(genreNbr)
            Else
                Return ""
            End If

        End Function

        Public Shared Function ProperName(ByVal strText As String) As String

            strText = strText.Trim

            If Right(strText, 4) = " The" Or _
                Right(strText, 3) = " El" Or _
                Right(strText, 3) = " La" Or _
                Right(strText, 4) = " Los" Or _
                Right(strText, 4) = " Las" Then
                strText = Mid(strText, strText.LastIndexOf(" ") + 1).Trim & " " & Mid(strText, 1, strText.LastIndexOf(" ")).Trim
            End If

            Return strText

        End Function

    End Class

End Namespace